package com.cui.project.service.impl;

import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollUtil;
import com.api.apiclientsdk.model.enums.APIConstant;
import com.api.common.constant.CommonConstant;
import com.api.common.model.entity.InterfaceInfo;
import com.api.common.model.entity.User;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.cui.project.mapper.UserMapper;
import com.cui.project.model.dto.interfaceInfo.InterfaceInfoQueryRequest;
import com.google.gson.Gson;
import com.api.common.common.ErrorCode;
import com.cui.project.exception.BusinessException;
import com.cui.project.mapper.InterfaceInfoMapper;
import com.cui.project.model.dto.interfaceInfo.InterfaceInfoEsDTO;
import com.cui.project.model.vo.InterfaceInfoRandVO;
import com.cui.project.service.InterfaceInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.RandomScoreFunctionBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author 86176
 * @description 针对表【interface_info(接口信息)】的数据库操作Service实现
 * @createDate 2024-09-26 11:22:31
 */
@Service
@Slf4j
public class InterfaceInfoServiceImpl extends ServiceImpl<InterfaceInfoMapper, InterfaceInfo>
        implements InterfaceInfoService {

    //    private final static String PATH_PATTERN = "/^\\/[a-zA-Z0-9_-]+(\\/[a-zA-Z0-9_-]+)*$/";
    private final static String PATH_PATTERN = "^\\/[a-zA-Z0-9_-]+(\\/[a-zA-Z0-9_-]+)*$";

    //    private final static String SERVER_ADDRESS_PATTERN = "/^(http:\\/\\/|https:\\/\\/)([a-zA-Z0-9.-]+|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(:[0-9]{1,5})?$/";
    private final static String SERVER_ADDRESS_PATTERN = "^(http:\\/\\/|https:\\/\\/)([a-zA-Z0-9.-]+|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(:[0-9]{1,5})?$";
    private final static List<String> METHOD_LIST = APIConstant.getValues();

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Resource
    private UserMapper userMapper;


    @Override
    public void validInterfaceInfo(InterfaceInfo interfaceInfo, boolean add) {
        if (interfaceInfo == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        String name = interfaceInfo.getName();
        String url = interfaceInfo.getUrl();
        String host = interfaceInfo.getHost();
        String requestHeader = interfaceInfo.getRequestHeader();
        String requestParams = interfaceInfo.getRequestParams();
        String responseHeader = interfaceInfo.getResponseHeader();
        String method = interfaceInfo.getMethod();
        String result = interfaceInfo.getResult();

        // 创建时，所有参数必须非空
        if (add) {
            if (StringUtils.isAnyBlank(name, url, host, requestHeader, responseHeader, method)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            if (name.length() > 50) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "名称过长");
            }
            if (!host.matches(SERVER_ADDRESS_PATTERN)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入有效的请求路径，例如 https://example.com");
            }
            if (!url.matches(PATH_PATTERN)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入有效的接口地址，例如  /api/v1/resource");
            }
            if (!METHOD_LIST.contains(method)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入有效的请求方法");
            }
            Gson gson = new Gson();
            try {
                gson.toJson(requestHeader);
                gson.toJson(responseHeader);
                gson.toJson(requestParams);
                if (StringUtils.isNotBlank(result)) gson.toJson(result);
            } catch (Exception e) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入有效的请求头/响应头/请求参数/返回结果格式(json)");
            }
        }
    }


    @Override
    public List<InterfaceInfoRandVO> rand(Integer size) {
        // 参数校验
        if (size == null || size <= 0) {
            size = 10;
        }
        if (size > 100) {  // 限制最大值
            size = 100;
        }
        try {
            //SearchSourceBuilder 用于构建es的搜索条件
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                    .size(size)  // 只获取一条数据
                    .query(
                            //用于创建搜索查询的实用程序类
                            QueryBuilders.functionScoreQuery(
                                            //matchAllQuery 要的是所有文档
                                            QueryBuilders.boolQuery()
                                                    .filter(QueryBuilders.termQuery("status",1)) //只查询上线接口
                                                    .filter(QueryBuilders.termQuery("isDelete",0)), //只查询未删除
                                            //为匹配的文档计算随机分数的函数
                                            new RandomScoreFunctionBuilder()  // 随机排序
                                                    .seed((int)(System.currentTimeMillis() / 60000))  // 每分钟变化一次
                                                    .setField("_seq_no")  // 使用序列号作为随机源
                                    )
                                    //Boost模式定义分数函数的组合结果将如何影响最终分数以及子查询分数。
                                    .boostMode(CombineFunction.REPLACE));  // 替换原有评分
            //构造查询
            NativeSearchQuery queryBuilder = new NativeSearchQueryBuilder()
                    .withQuery(searchSourceBuilder.query()).build();
            SearchHits<InterfaceInfoEsDTO> searchHits = elasticsearchRestTemplate.search(queryBuilder, InterfaceInfoEsDTO.class);

            List<InterfaceInfoRandVO> resourceList = new ArrayList<>();
            //查询是否有数据
            if(!searchHits.hasSearchHits()){
                return resourceList;
            }

            List<SearchHit<InterfaceInfoEsDTO>> searchHitList = searchHits.getSearchHits();

            List<Long> interfaceIdList = searchHitList.stream().map(hit -> hit.getContent().getId()).collect(Collectors.toList());

            //动静分离设计：先模糊筛选静态数据，查出数据后，再根据查到的内容 id 去数据库查找到 动态数据
            List<InterfaceInfoRandVO> interfaceInfos = this.baseMapper.rand(interfaceIdList);

            if (CollUtil.isEmpty(interfaceInfos)) {
                return resourceList;
            }
            Set<Long> userIdSet = interfaceInfos.stream().map(InterfaceInfoRandVO::getUserId).collect(Collectors.toSet());
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(User::getId,userIdSet);
            queryWrapper.select(User::getId,User::getUserName);
            List<User> userList = userMapper.selectList(queryWrapper);
            Map<Long, List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getId));

            Map<Long, List<InterfaceInfoRandVO>> idInterfaceInfoMap = interfaceInfos.stream().collect(Collectors.groupingBy(InterfaceInfoRandVO::getId));

            //按ES查询顺序返回结果
            interfaceIdList.forEach(interfaceInfoId -> {
                if (idInterfaceInfoMap.containsKey(interfaceInfoId)) {
                    InterfaceInfoRandVO interfaceInfoRandVO = idInterfaceInfoMap.get(interfaceInfoId).get(0);
                    Long userId = interfaceInfoRandVO.getUserId();
                    List<User> users = userMap.get(userId);
                    if(users!=null&&users.isEmpty())interfaceInfoRandVO.setUserName(users.get(0).getUserName());
                    resourceList.add(interfaceInfoRandVO);
                }
//                else {
//                    String delete = elasticsearchRestTemplate.delete(String.valueOf(interfaceInfoId), InterfaceInfoEsDTO.class);
//                    log.info("删除es中interfaceInfo 数据 {}", delete);
//                }
            });
            Collections.shuffle(resourceList);
            return  resourceList;

        }catch (Exception e){
            log.error("ES查询 rand 接口失败：size={}",size,e);
            //降级为数据库随机查询
            return fallbackRand(size);
        }
    }


    @Cacheable(value = "api:interface:rand",key="$size",unless = "$resulit.isEmpty()")
    public List<InterfaceInfoRandVO> fallbackRand(Integer size) {
        try {
            QueryWrapper<InterfaceInfo> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("status",1);
            queryWrapper.eq("isDelete",0);
            queryWrapper.orderByDesc("rand()");
            queryWrapper.last("limit"+size);
            List<InterfaceInfo> list = this.list(queryWrapper);
            return list.stream()
                    .map(info->{
                        InterfaceInfoRandVO randVO = new InterfaceInfoRandVO();
                        BeanUtils.copyProperties(info,randVO);
                        return randVO;
                    }).collect(Collectors.toList());

        }catch (Exception e){
            log.error("数据库查询 rand 接口失败",e);
            return new ArrayList<>();
        }
    }

    @Override
    public Page<InterfaceInfo> searchFromEs(InterfaceInfoQueryRequest interfaceInfoQueryRequest) {
        Long id = interfaceInfoQueryRequest.getId();
        String searchText = interfaceInfoQueryRequest.getSearchText();
        String name = interfaceInfoQueryRequest.getName();
        String description = interfaceInfoQueryRequest.getDescription();
        String url = interfaceInfoQueryRequest.getUrl();
        String requestHeader = interfaceInfoQueryRequest.getRequestHeader();
        String requestParams = interfaceInfoQueryRequest.getRequestParams();
        String responseHeader = interfaceInfoQueryRequest.getResponseHeader();
        String imageUrl = interfaceInfoQueryRequest.getImageUrl();
        String host = interfaceInfoQueryRequest.getHost();
        String resultType = interfaceInfoQueryRequest.getResultType();
        String result = interfaceInfoQueryRequest.getResult();
        Integer status = interfaceInfoQueryRequest.getStatus();
        String method = interfaceInfoQueryRequest.getMethod();
        Long userId = interfaceInfoQueryRequest.getUserId();
        // es 起始页为 0
        long current = interfaceInfoQueryRequest.getCurrent() - 1;
        long pageSize = interfaceInfoQueryRequest.getPageSize();
        String sortField = interfaceInfoQueryRequest.getSortField();
        String sortOrder = interfaceInfoQueryRequest.getSortOrder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //过滤出 isDelete 为0的数据
        boolQueryBuilder.filter(QueryBuilders.termQuery("isDelete", 0));
        if (id != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("id", id));
        }
        // 按关键词检索
        if (StringUtils.isNotBlank(searchText)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("name", searchText));
            boolQueryBuilder.should(QueryBuilders.matchQuery("description", searchText));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        if (StringUtils.isNotBlank(name)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("name", name));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        if (StringUtils.isNotBlank(description)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("description", description));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        //排序
        SortBuilder<?> sortBuilder = SortBuilders.scoreSort();
        if (StringUtils.isNotBlank(sortField)) {
            sortBuilder = SortBuilders.fieldSort(sortField);
            sortBuilder.order(CommonConstant.SORT_ORDER_DESC.equals(sortOrder) ? SortOrder.DESC : SortOrder.ASC);
        }
        //分页
        PageRequest pageRequest = PageRequest.of((int) current, (int) pageSize);
        //构造查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withPageable(pageRequest)
                .withSorts(sortBuilder)
                .withTimeout(Duration.ofSeconds(3)) //设置超时时间
                .build();
        SearchHits<InterfaceInfoEsDTO> searchHits = elasticsearchRestTemplate.search(searchQuery, InterfaceInfoEsDTO.class);
        Page<InterfaceInfo> page = new Page<>();
        page.setTotal(searchHits.getTotalHits());
        List<InterfaceInfo> resourceList = new ArrayList<>();
        //查询是否有数据
        if (searchHits.hasSearchHits()) {
            List<SearchHit<InterfaceInfoEsDTO>> searchHitList = searchHits.getSearchHits();
            List<Long> interfaceIdList = searchHitList.stream().map(hit -> hit.getContent().getId()).collect(Collectors.toList());
            //动静分离设计：先模糊筛选静态数据，查出数据后，再根据查到的内容 id 去数据库查找到 动态数据
            List<InterfaceInfo> interfaceInfos = this.baseMapper.selectBatchIds(interfaceIdList);
            if (CollUtil.isNotEmpty(interfaceInfos)) {
                /**
                 * 这边对从数据库查询到的动态数据进行分组
                 * 如果idInterfaceInfoMap.containsKey(interfaceInfoId)在动态数据中的数据id不存在
                 * 就说明es中的这条数据应该删除，如果存在就从动态数据中用对应id获取数据
                 */
                Map<Long, List<InterfaceInfo>> idInterfaceInfoMap = interfaceInfos.stream().collect(Collectors.groupingBy(InterfaceInfo::getId));
                interfaceIdList.forEach(interfaceInfoId -> {
                    if (idInterfaceInfoMap.containsKey(interfaceInfoId)) {
                        resourceList.add(idInterfaceInfoMap.get(interfaceInfoId).get(0));
                    } else {
                        String delete = elasticsearchRestTemplate.delete(String.valueOf(interfaceInfoId), InterfaceInfoEsDTO.class);
                        log.info("删除es中interfaceInfo 数据 {}", delete);
                    }
                });
            }
        }
        page.setRecords(resourceList);
        return page;
    }

    @Override
    public List<InterfaceInfo> getNames(Long userId) {
        return this.baseMapper.getTags(userId);
    }

    /**
     * 判断是否属于当前用户上传的接口
     * @param userId
     * @param interfaceId
     * @return
     */
    @Override
    public boolean isInfo(Long userId, Long interfaceId) {
        LambdaQueryWrapper<InterfaceInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(InterfaceInfo::getId,interfaceId);
        queryWrapper.eq(InterfaceInfo::getUserId,userId);

        return this.baseMapper.exists(queryWrapper);
    }

    @Override
    public List<Long> useInterfaceIds(Long userId) {


        return this.baseMapper.byUseInterfaceId(userId);
    }
}




